﻿namespace GoodAdmin.System;

/// <inheritdoc cref="ISysRoleService"/>
public class SysRoleService : DbRepository<SysRole>, ISysRoleService, ITransient
{
    private readonly IRelationService _relationService;
    private readonly IRedisCacheManager _redisManager;
    private readonly IMenuService _menuService;

    public SysRoleService(IRelationService relationService, IRedisCacheManager redisManager,IMenuService menuService)
    {
        _relationService = relationService;
        _redisManager = redisManager;
        _menuService = menuService;
    }

    /// <inheritdoc/>
    public async Task AddRole(RoleAddInput input)
    {
        await CheckInput(input,CommonConst.ACTION_INSERT);
        var role = input.Adapt<SysRole>();
        //默认为有效
        role.Status = input.Status ?? CommonConst.STATUS_ENABLE;
        //数据范围默认为本部门
        role.DataScope = input.DataScope ?? RoleConst.DATA_SCOPE_OWNORG;
        if(await InsertAsync(role))
        {
            //删除缓存
            RefreshCahce();
        }            
    }

    /// <inheritdoc/>
    public async Task UpdateRole(RoleUpdateInput input)
    {        
        await CheckInput(input,CommonConst.ACTION_UPDATE);      
        var role = input.Adapt<SysRole>();
        if(await UpdateAsync(role))
        {
            //删除缓存
            RefreshCahce();
            await RefresUserCache(new List<long> { role.Id });
        }
    }

    /// <inheritdoc/>
    public async Task DeleteRole(List<BaseIdInput> input)
    {
        var ids = input.Select(i => i.Id).ToList();
        if(await IsAnyAsync(r=>r.Code==RoleConst.SUPERADMIN && ids.Contains(r.Id)))
        {
            throw Oops.Bah(RoleErrorCode.R004);
        }
        var res = await Context.Ado.UseTranAsync(async () =>
        {
            //删除角色
            await DeleteByIdsAsync(ids.Cast<object>().ToArray());
            //删除角色菜单信息
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_MENU, ids);
            //删除角色用户信息
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_USER, ids);
            //删除角色资源信息
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_RESOURCE, ids);
        });
        if (res.IsSuccess)
        {
            //删除缓存
            RefreshCahce();           
            await RefresUserCache(ids);
        }            
    }

    /// <inheritdoc/>
    public async Task<List<SysRole>> GetUserRoleList(long userId)
    {
        var roleIdList = await GetUserRoleIdList(userId);
        if (roleIdList.Count == 0) return new List<SysRole>();
        var roleList = await GetRoleList();        
        return roleList.Where(r => roleIdList.Contains(r.Id)).ToList();
    }

    /// <inheritdoc/>
    public async Task<List<long>> GetUserRoleIdList(long userId)
    {
        var relationList = await _relationService.GetRelationListByTargetId(CategoryConst.RELATION_ROLE_USER, userId);
        return relationList.Select(r => r.ObjectId).ToList();
    }

    /// <inheritdoc/>
    public async Task<List<SysRole>> GetRoleList()
    {
        var key = CacheConst.SysRole;

        var roleList = _redisManager.Get<List<SysRole>>(key);
        if(roleList== null)
        {
            roleList = await GetListAsync();
            _redisManager.Set(key, roleList);
        }
        return roleList;
    }

    /// <inheritdoc/>
    public async Task<SqlSugarPagedList<SysRole>> GetRolePage(RolePageInput input)
    {
        return await Context.Queryable<SysRole>()
            .WhereIF(!string.IsNullOrWhiteSpace(input.RoleName), r => r.RoleName.Contains(input.RoleName))
            .OrderByIF(!string.IsNullOrWhiteSpace(input.SortField), r => $"{input.SortField} {input.SortOrder}")
            .OrderBy(r=>r.OrderField)
            .ToPagedListAsync(input.PageIndex, input.PageSize);
    }
    /// <inheritdoc/>
    public async Task<List<RoleSelectorOutput>> GetRoleSelector(RolePageInput input)
    {
        var roleList = await GetRoleList();
        var res = roleList.WhereIF(!string.IsNullOrWhiteSpace(input.RoleName), r => r.RoleName.Contains(input.RoleName))
            .OrderBy(r => r.OrderField).Adapt<List<RoleSelectorOutput>>();
        return res;
    }
    /// <inheritdoc/>
    public async Task<List<string>> GetResource(BaseIdInput input)
    {
        //获取角色关联菜单
        var roleMenuList = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_MENU, input.Id);        
        //根据菜单路由获取对应资源
        var apiResource = await GetResource(roleMenuList.Select(it => it.TargetId).ToList());
        return apiResource.Select(it=>it.ResourceName).ToList();
    }    

    /// <inheritdoc/>
    public async Task<RoleResourceOutput> GetRoleOwnResource(BaseIdInput input)
    {
        var relation = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_RESOURCE, input.Id);
        var roleResourceJson = relation.Select(it => it.ExtraJson).FirstOrDefault();
        var roleResource= roleResourceJson.ToJsonEntity<List<ResourceOutput>>();
        return new RoleResourceOutput { Id = input.Id, Resources = roleResource };
    }

    /// <inheritdoc/>
    public async Task<List<long>> GetRoleOweMenu(BaseIdInput input)
    {
        var relation = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_MENU, input.Id);
        return relation.Select(it => it.TargetId).ToList();
    }

    /// <inheritdoc/>
    public async Task<List<long>> GetRoleOwnUser(long roleId)
    {
        var relation = await _relationService.GetRelationListByObjectId(CategoryConst.RELATION_ROLE_USER, roleId);
        return relation.Select(it=>it.TargetId).ToList(); 
    }

    /// <inheritdoc/>
    public async Task GrantRoleMenus(RoleMenusInput input)
    {              
        var sysRole = (await GetRoleList()).Where(r => r.Id == input.Id).First();
        if (sysRole == null) return;
        //分配菜单时，同时分配资源
        var resource = await GetResource(input.MenuList);
        var resourceList= new List<ResourceOutput>();
        resource.ForEach(it =>
        {
            resourceList.Add(new ResourceOutput
            {
                ApiRoute=it.ApiRoute,
                DataScopeType=sysRole.DataScope,
                DefineScopeOrgList=sysRole.DefineScopeOrgList
            });
        });
        var res= await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_MENU, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_MENU, input.Id, input.MenuList);
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_RESOURCE, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_RESOURCE, input.Id, input.Id, resourceList.ToJson());
        });
        
        if (res.IsSuccess)
        {

            await RefresUserCache(new List<long> { input.Id });
            //如果当前角色为系统基础角色,那么需要移除CommonRoleMenu缓存           
            if ((await GetRoleList()).Any(r => r.Code == RoleConst.ROLE_COMMON && r.Id == input.Id))
                _redisManager.Remove(CacheConst.CommonRoleMenu);
        }
    }

    /// <inheritdoc/>
    public async Task GrantRoleUsers(RoleUsersInput input)
    {
        var res = await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_USER, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_USER, input.Id, input.UserList);
        });
        if (res.IsSuccess)
        {
            //删除缓存
            await RefresUserCache(new List<long> { input.Id });
        }         
    }

    /// <inheritdoc/>
    public async Task GrantRoleResource(RoleResourceInput input)
    {
        input.Resources.ForEach(it =>
        {
            if (string.IsNullOrWhiteSpace(it.DataScopeType))
            {
                throw Oops.Bah(RoleErrorCode.R006, it.ApiRoute);
            }
            if (it.DataScopeType == RoleConst.DATA_SCOPE_DEFINE && it.DefineScopeOrgList.Count == 0)
            {
                throw Oops.Bah(RoleErrorCode.R006);
            }
        });
        var json = input.Resources.ToJson();
        var res = await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndObjectId(CategoryConst.RELATION_ROLE_RESOURCE, input.Id);
            await _relationService.AddRelation(CategoryConst.RELATION_ROLE_RESOURCE, input.Id, input.Id, json);
        });
        if(res.IsSuccess)
        {
            await RefresUserCache(new List<long> { input.Id });
        }                     
    }

    /// <inheritdoc/>
    public async Task<List<SysMenu>> GetCommonRoleMenu()
    {
        string key = CacheConst.CommonRoleMenu;
        var menuList = _redisManager.Get<List<SysMenu>>(key);
        if (menuList == null)
        {
            //先获取基础权限
            var commonRole = (await GetRoleList()).First(r => r.Code == RoleConst.ROLE_COMMON);
            //根据权限Id
            menuList = await _menuService.GetMenuListByRoleId(commonRole.Id);
            _redisManager.Set(key, menuList);
        }
        return menuList;
    }

    /// <summary>
    /// 检查输入
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    private async Task CheckInput(SysRole input, string action)
    {
        if (input.DataScope == RoleConst.DATA_SCOPE_DEFINE && input.DefineScopeOrgList.Count==0)
        {
            throw Oops.Bah(RoleErrorCode.R006);
        }

        var roleList = await GetRoleList();
        //判断是否有同名角色
        if (roleList.Any(r => r.RoleName == input.RoleName && input.Id != r.Id))
        {
            throw Oops.Bah(RoleErrorCode.R001);
        }
        //判断是否有相同角色编码(忽略大小写)
        if (roleList.Any(r => r.Code.EqualIgnoreCase(input.Code) && input.Id != r.Id))
        {
            throw Oops.Bah(RoleErrorCode.R008);
        }
        if (action == CommonConst.ACTION_UPDATE)
        {
            var role = roleList.FirstOrDefault(r => r.Id == input.Id) ?? throw Oops.Bah(RoleErrorCode.R009);
            if (role.SystemMark == CommonConst.SYSTEM && (role.RoleName != input.RoleName || role.Code != input.Code))
            {
                throw Oops.Bah(RoleErrorCode.R009);
            }
        }
    }

    /// <summary>
    /// 通过指定菜单获取资源
    /// </summary>
    /// <param name="MenuList"></param>
    /// <returns></returns>
    private async Task<List<ApiResource>> GetResource(List<long> MenuList)
    {
        //获取菜单列表
        var menuList = await _menuService.GetMenuListByMenuId(MenuList);
        //根据菜单路由获取对应资源
        var apiResource = _menuService.GetResource(menuList.Select(it => it.Path).ToList());
        return apiResource;
    }
    private void RefreshCahce()
    {
        _redisManager.Remove(CacheConst.SysRole);
    }
    private async Task RefresUserCache(List<long> roleIds)
    {       
        var relationList = await _relationService.GetRelationListByObjectIdList(CategoryConst.RELATION_ROLE_USER, roleIds);
        var userIds = relationList.Select(x => x.TargetId).Distinct().ToList();
        //移除角色对应的用户所拥有的数据范围缓存
        foreach (var userId in userIds)
        {
            var key = CacheConst.DataScope + userId;
            _redisManager.Remove(key);
        }        
    }
}